package org.codingq92.hand.aqs;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;

public class MyAQSLock {

    boolean failLock;

    // 锁状态
    AtomicInteger state = new AtomicInteger(0);
    // 当前拿到锁的线程
    Thread owner;
    // 头节点
    AtomicReference<Node> head = new AtomicReference<>(new Node());
    // 尾节点
    AtomicReference<Node> tail = new AtomicReference<>(head.get());

    public MyAQSLock(boolean isFail) {
        failLock = isFail;
    }

    public void lock() {
        // cas 尝试获取锁 这个代码就是非公平锁的实现 不执行就变公平锁了
        if (!failLock) {
            if (state.compareAndSet(0, 1)) {
                System.out.println("lock: 当前线程" + Thread.currentThread().getName() + ">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 直接拿到锁");
                owner = Thread.currentThread();
                return;
            }
        }
        // 获取锁失败的当前节点
        Node current = new Node();
        current.thread = Thread.currentThread();
        // 获取当前尾节点 并将当前未获取到锁的节点变为尾节点~
        while (true) {
            Node currentTail = tail.get();
            if (tail.compareAndSet(currentTail, current)) {
                System.out.println("lock: 当前线程" + Thread.currentThread().getName() + ">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 加入队列");
                current.prev = currentTail;
                currentTail.next = current;
                break;
            }
        }
        // 阻塞自己并等待唤醒 + 获取锁
        while (true) {
            // 再次尝试获取锁 获取不到 再阻塞自己(当前节点的前置节点=头节点 && 头节点cas获取锁成功)
            if (current.prev == head.get() && state.compareAndSet(0, 1)) {
                owner = Thread.currentThread();
                head.set(current);
                current.prev.next = null;
                current.prev = null;
                System.out.println("lock: 当前线程" + Thread.currentThread().getName() + ">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 唤醒获取锁成功");
                return;
            }
            // 阻塞自己等待唤醒
            LockSupport.park();
        }
    }

    public void unlock() {
        if (owner != Thread.currentThread()) {
            throw new RuntimeException("当前线程不是持有锁的线程");
        }
        // 需要开始唤醒后续的节点去拿锁
        Node headNode = head.get();
        Node next = headNode.next; // 需要唤醒的节点
        // 唤醒之前得先将之前的锁状态复位
        state.set(0);
        // 只要head节点的后续节点不是空 就说明队列有需要被唤醒的节点
        // 我们只需要唤醒head节点的next节点即可
        if (next != null) {
            System.out.println("unlock: 唤醒线程" + next.thread.getName());
            LockSupport.unpark(next.thread);
        }
    }

    class Node {
        // 后续节点
        volatile Node next;
        // 前续节点
        volatile Node prev;
        // 当前线程
        volatile Thread thread;
    }
}
